package top.peng.concurrency.example.h_juc_extend;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * java7提供的并行执行任务的框架
 * 思想和MapReduce了类似，fork就是分解任务，join就是合并任务
 * 底层使用的工作窃取算法，任务队列为双端队列，减少线程中的竞争
 *
 * 局限性：
 * 1、任务只能使用fork和join操作，来作为同步机制 比如使任务进行了睡眠，正在执行这个任务的工作线程，将不会执行其他任务了
 * 2、我们所拆的任务，不应该去执行I/O操作
 * 3、任务不能抛出检查异常，必须通过代码来处理他们
 *
 * RecursiveTask 就是递归调用的意思，类比递归排序
 *
 */
@Slf4j
public class ForkJoinTaskSample extends RecursiveTask<Integer> {

    public static final int THRESHOLD = 2;
    private int start;
    private int end;

    public ForkJoinTaskSample(int start, int end) {
        this.start = start;
        this.end = end;
    }

    /**
     * 使用fork/join框架进行计算
     * @return
     */
    @Override
    protected Integer compute() {
        int sum = 0;

        // 如果任务足够小就直接进行计算
        boolean canCompute = (end -start) <= THRESHOLD;
        if (canCompute) {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        } else {
            // 如果任务量大于阈值，就分裂成两个子任务计算
            int middle = (start + end) / 2;
            ForkJoinTaskSample leftTask = new ForkJoinTaskSample(start, middle);
            ForkJoinTaskSample rightTask = new ForkJoinTaskSample(middle + 1, end);

            // 执行子任务
            leftTask.fork();
            rightTask.fork();

            // 等待任务执行结束合并其结果
            Integer leftResult = leftTask.join();
            Integer rightResult = rightTask.join();

            // 合并子任务
            sum = leftResult + rightResult;
        }
        return sum;
    }

    /**
     * 直接使用for循环来进行累加
     * @param start
     * @param end
     * @return
     */
    private static Integer forCompute(int start, int end) {
        long startTime = System.currentTimeMillis();
        int sum = 0;
        for (int i = 0; i <= end; i++) {
            sum += i;
        }
        log.info("forCompute 耗时：{} ms", (System.currentTimeMillis() - startTime));

        return sum;
    }

    public static void main(String[] args) {

        log.info("Runtime.getRuntime().availableProcessors(): {}", Runtime.getRuntime().availableProcessors());

        long startTime = System.currentTimeMillis();
        // 创建forkjoin线程池
        ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
        // 创建一个计算任务
        ForkJoinTaskSample task = new ForkJoinTaskSample(1, 10000000);
        // 执行任务
        ForkJoinTask<Integer> result = forkJoinPool.submit(task);
        try {
            log.info("ForkJoin 耗时：{} ms", (System.currentTimeMillis() - startTime));
            log.info("ForkJoin result:{}", result.get());
        } catch (Exception e) {
            log.error("ForkJoin exception", e);
        }

        log.info("forCompute result:{}", forCompute(1, 10000000));

        /**
         * 验证结果：当计算任务越大时，就能验证到使用多线程的优势
         * 如果计算量很小，使用多线程来做计算，反而会占用系统资源
         */
    }



}
